#!/usr/bin/env ruby
require 'rubygems'
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", 'lib')))
require "bundler/setup"
require 'rdf/rdfa'
require 'sparql/grammar'
require File.expand_path(File.join(File.dirname(__FILE__), "..", 'spec', 'spec_helper'))
require File.expand_path(File.join(File.dirname(__FILE__), "..", 'spec', 'suite_helper'))
require 'getoptlong'

def run_tc(tc, options)
  input_uri = tc.input(options[:host_language], options[:version])
  results_uri = tc.results(options[:host_language], options[:version])
  res = nil

  STDOUT.write "run #{tc.num}"

  if options[:verbose]
    puts("\nTestCase: #{tc.inspect}") 
    puts("\nInput:\n" + Kernel.open(input_uri) {|f| f.read}) 
    puts("\nQuery:\n" + Kernel.open(results_uri) {|f| f.read})
  end

  pg = RDF::Graph.new if options[:processor_graph]

  begin
    puts "open #{input_uri}" if options[:verbose]
    options = {
      base_uri: input_uri,
      processor_graph: pg,
      format: :rdfa,
    }.merge(options)

    if tc.queryParam
      opt, arg = tc.queryParam.split('=').map(&:to_sym)
      options[opt] = arg
    end

    reader = RDF::RDFa::Reader.new(Kernel.open(input_uri), options)

    graph = RDF::Repository.new << reader
  rescue Exception => e
    options[:result_count]["exception"] ||= 0
    options[:result_count]["exception"] += 1
    puts "#{"exception:" unless options[:quiet]}: #{e}"
    if options[:quiet]
      return
    else
      raise
    end
  end
  
  puts("\nResult:\n" + graph.dump(options[:format])) unless options[:quiet]
  
  begin
    result = SPARQL::Grammar.parse(Kernel.open(results_uri)).execute(graph)
  rescue Exception => e
    options[:result_count]["exception"] ||= 0
    options[:result_count]["exception"] += 1
    puts "#{"exception:" unless options[:quiet]}: #{e}"
    return
  end
  res = (result == (tc.expectedResults || true)) ? 'pass' : 'fail'
  puts "#{"test result:" unless options[:quiet]} #{res}"
  options[:result_count][res] ||= 0
  options[:result_count][res] += 1

  if pg && !options[:quiet]
    puts "\nProcessor Graph:\n"
    puts pg.inspect
    RDF::Writer.for(options[:format]).new do |writer|
      writer << pg
    end
  end
end

logger = Logger.new(STDERR)
logger.level = Logger::WARN
logger.formatter = lambda {|severity, datetime, progname, msg| "#{severity}: #{msg}\n"}

options = {
  verbose:         false,
  quite:           false,
  validate:        false,
  format:          :ntriples,
  library:         nil,
  processor_graph: nil,
  host_language:   "xhtml1",
  version:         "rdfa1.1",
  logger:          logger
}

opts = GetoptLong.new(
  ["--help", "-?", GetoptLong::NO_ARGUMENT],
  ["--dbg", GetoptLong::NO_ARGUMENT],
  ["--format", GetoptLong::REQUIRED_ARGUMENT],
  ["--host-language", "-h", GetoptLong::REQUIRED_ARGUMENT],
  ["--library", GetoptLong::REQUIRED_ARGUMENT],
  ["--processor-graph", GetoptLong::NO_ARGUMENT],
  ["--quiet", GetoptLong::NO_ARGUMENT],
  ["--validate", GetoptLong::NO_ARGUMENT],
  ["--verbose", GetoptLong::NO_ARGUMENT],
  ["--version", "-v", GetoptLong::OPTIONAL_ARGUMENT]
)

def help(options)
  puts "Usage: #{$0} [options] [test-number ...]"
  puts "Options:"
  puts "      --dump:               Dump raw output, otherwise serialize to Ruby"
  puts "      --dbg:                Display detailed debug output"
  puts "      --expand              Expand graph with vocab_expansion option"
  puts "      --format:             Format for output, defaults to #{options[:format].inspect}"
  puts "      --host-language:      Run for specified host language, defaults to #{options[:host_language]}"
  puts "      --library:            XML parsing library, defaults to :nokogiri if available, otherwise :rexml"
  puts "      --rdfagraph:          output, processor or output,processor"
  puts "      --quiet:              Minimal output"
  puts "      --validate:           Validate input"
  puts "      --verbose:            Verbose processing"
  puts "      --version:            Version of processor to use (rdf1.0, rdf1.1). Defaults to #{options[:version]}"
  puts "      --help,-?:            This message"
  exit(0)
end

opts.each do |opt, arg|
  case opt
  when '--help'             then help(options)
  when '--dbg'              then logger.level = Logger::DEBUG
  when '--format'           then options[:format] = arg.to_sym
  when '--host-language'    then options[:host_language] = arg
  when '--library'          then options[:library] = arg.to_sym
  when '--quiet'
    options[:quiet] = true
    logger.level = Logger::FATAL
  when '--validate'         then options[:validate] = true
  when '--verbose'          then options[:verbose] = true
  when '--version'          then options[:version] = arg
  end
end

result_count = {}

Fixtures::TestCase.for_specific(options[:host_language], options[:version]) do |tc|
  next unless ARGV.empty? || ARGV.any? {|n| tc.num.match(/#{n}/)}
  next if tc.classification.to_s =~ /deprecated/
  run_tc(tc, options.merge(result_count: result_count))
end

result_count.each do |result, count|
  puts "#{result}: #{count}"
end
